/**
 * Connect 4   Based on a demo by Yngvi Bjornsson
 */
import java.util.ArrayList;

enum Player { Black, Red }
enum Result { Unknown, Draw, WinBlack, WinRed }

public class State {
	
	enum Square { Empty, BlackDisk, RedDisk }

    // Win/Loss values
    public static int WIN_VALUE  =  1000;
    public static int LOSS_VALUE = -1000;
    
    public Square board[][];
    public Player playerToMove = null; 
    public Result resultState = null;
    public int numPiecesInBoard = 0;
    public Move lastMove = null;
    public int lastRaw = 0;
    
    // Add any private variables you may need here (optional, but you'll
    // probably need to)

    public State( int row, int col )
    {
    	this.board = new Square[row][col];
    	for (int r=0; r<row;r++){
    		for (int c=0; c<col;c++){
    			this.board[r][c] = Square.Empty;
    		}
    	}
    	this.playerToMove = Player.Black;
    	this.resultState = Result.Unknown;
    	this.numPiecesInBoard = 42;
    	this.lastMove = null;
    	this.lastRaw = 0;
    }

    public void reset()
    {
    	//Reset the state to an empty board and black has move
    	for (int r=0; r<this.board.length;r++){
    		for (int c=0; c<this.board[0].length;c++){
    			this.board[r][c] = Square.Empty;
    		}
    	}
    	this.playerToMove = Player.Black;
    	this.resultState = Result.Unknown;
    }

    public Player getPlayerToMove( )
    {
        //  Return the player who has next move
    	if (this.playerToMove == Player.Black )
    		return Player.Red;
    	else
    		return Player.Black;
    }

    // Get all possible actions in the state.
    public ArrayList<Move> getActions( )
    {
    	//  Construct a list of the possible moves from the current
    	// state and return it as an ArrayList
    	ArrayList<Move> actions = new ArrayList<Move>();
    	for (int c=0; c < this.board[0].length; c++){
    		if(this.board[0][c]== Square.Empty){
    			actions.add(new Move(c));
    		}
    	}
    	return actions;
    }


    public void make( Move move )
    {
    	//  Update the state by making a move. Remember to switch
    	// the player who has the next move.
    	for (int r=0; r<this.board.length; r++ ){
    		if (this.board[r][move.col]==Square.Empty){
    			if(this.playerToMove == Player.Black){
    				this.board[r][move.col] = Square.BlackDisk ;
    				this.playerToMove = Player.Red;
    			}
    			else{
    				this.board[r][move.col] = Square.RedDisk ;
    				this.playerToMove = Player.Black;
    			}
    			this.numPiecesInBoard --;
    			this.lastMove = move;
    			this.lastRaw = r;
    			return;
    		}
    	}
    	
    }


    public void retract( Move move )
    {
    	// Update the state by retracting (undoing) a move. Remember
    	// to switch the player who has the next move.
    	for (int r=this.board.length -1 ; r == 0; r-- ){
    		if (this.board[r][move.col]!=Square.Empty){
    			this.board[r][move.col] = Square.Empty;
    			this.numPiecesInBoard++;
    			if(this.playerToMove == Player.Black){
    				this.playerToMove = Player.Red;
    			}
    			else{
    				this.playerToMove = Player.Black;
    			}
    		}
    	}
    }


    public boolean isTerminal()
    {
        // return true if and only if the current state is a terminal
    	// one, i.e. either player has won or the board is full
    	final Square diskCheck;
    	int raw = this.board.length;
    	int col = this.board[0].length;
    	if (this.numPiecesInBoard == 0){
    		// The board is full
    		return true;
    	}
    	if (this.playerToMove == Player.Black){
    		diskCheck = Square.RedDisk; 
    	}
    	else{
    		diskCheck = Square.BlackDisk;
    	}
    	//check 4 in vertical 
    	int cont = 1;
    	int x = this.lastRaw;
    	while(++ x < raw && this.board[x][this.lastMove.col] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	//check 4 in orizontal
    	cont = 1; 
    	int y = this.lastMove.col;
    	//check on the right of the last piece insert.
    	while(++y < col && this.board[this.lastRaw][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	y = this.lastMove.col;
    	// check on the left of the last piece insert.
    	while(--y > 0 && this.board[this.lastRaw][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	//check 4 in the diagonal.
    	cont = 1;
    	// for this kind of diagonal " \ "
    	x = this.lastRaw;
    	y = this.lastMove.col;
    	
    	while(++y < col && ++ x < raw && this.board[x][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	x = this.lastRaw;
    	y = this.lastMove.col;
    	
    	while(--y > 0 && --x > 0 &&this.board[x][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	// for this kind of diagonal " / "
    	cont = 1;
    	x = this.lastRaw;
    	y = this.lastMove.col;
    	while(--x > 0 && ++y < col && this.board[x][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	x = this.lastRaw;
    	y = this.lastMove.col;
    	while(--y > 0 && ++x < raw && this.board[x][y] == diskCheck){
    		cont++;
    		if (cont==4)
    			return true;
    	}
    	//is not terminal
    	return false;
    }

    public Result getResult()
    {
    	// return the result of a game. The result is "Unkown" if the
    	// state is not terminal, "WinBlack" or "WinRed" if there are four
    	// pieces of the same colour in a row somewhere, or "Draw" otherwise
    	// and the board is full
    	return this.resultState;
    }


    public Move isLegalMove( String strMove )
    {
    	// This function takes a string representing a move, and returns the
    	// Move object if it is a legal one, otherwise it returns null.
        ArrayList<Move> moves = getActions();
        for( Move move : moves ) {
            if ( move.toStr().equals( strMove ) ) {
                return move;
            }
        }
        return null;
    }

    public int getEvaluation()
    {
        int value = 0;

        // Evaluate position (from Black's perspective).
        if ( isTerminal() ) {
            Result result = getResult();
            if ( result == Result.Draw ) {
               value = 0;
            }
            else if ( result == Result.WinBlack ) {
               value = State.WIN_VALUE;
            }
            else if ( result == Result.WinRed ) {
               value = State.LOSS_VALUE;
            }
            else {
                System.out.println("Error: should not happen.");
                value = 0;
            }
        } else {
            // TODO: Add your code here. The code should evaluate a non-terminal
        	// state and return a number in the range LOSS_VALUE to WIN_VALUE,
        	// indicating how good we *estimate* this state to be.
        	// Remember to evaluate from Black's perspective, no matter whose move
        	// it is - as the last line in this function takes care of reversing
        	// the value if Red has next move.
        	//
        	// NOTE: There is no harm in skipping this TODO until everything else is
        	// finished (both in this file and Search.java), and it is even advisable
        	// to do so.
        }

        // Return value relative from the side to move.
        return ( getPlayerToMove() == Player.Black ) ? value : -value;
    }


	public Square[][] getBoard() {
	    // Get the state represented as a 6 element array of rows,
	    // where each row is a 7 element array of Square. First row
	    // is the bottom row, and first column is the leftmost one.
	    // Example:
	    // { { RedDisk, RedDisk, BlackDisk, Empty, BlackDisk, Empty, Empty },  // bottom row
	    //   { Empty, RedDisk, Empty, BlackDisk, Empty, Empty, Empty },
	    // ...
	    // This is used in the GUI and to display the state	
		return this.board;
	}
	
	//  Add any private helper functions you may need here. (optional)
	
}
